/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "TaskQueue.h"
#include "Cache.h"

#include "myassert.h"
#include "StringBuilder.h"
#include "config.h"

using namespace Chronos;

//------ TaskTime ------
class Chronos::TaskQueue::TaskTime {
private:
    Task* last;
    TaskTime(const TaskTime&);
    TaskTime& operator=(const TaskTime&);
    static Cache<TaskTime, 20> cache;
public:
    Task* first;

    TaskTime() : last(0), first(0) {
    }
    ~TaskTime() {
        while (first) {
            Task* cur = first;
            first = first->next;
            delete cur;
        }
    }
    void addTask(Task* newTask) {
        if (!first) first = newTask;
        else last->next = newTask;
        last = newTask;
        newTask->next = 0;
    }
    void* operator new(size_t) {
        return cache.get();
    }
    void operator delete(void* ptr) {
        cache.put(ptr);
    }
};

Chronos::Cache<Chronos::TaskQueue::TaskTime, 20> Chronos::TaskQueue::TaskTime::cache;


//------ TaskQueue ------
TaskQueue::TaskQueue(unsigned int startTime)
    : currentTime(startTime)
    , taskCount(0)
{}

TaskQueue::~TaskQueue() {
    for (TaskTimesConstIter iter=times.begin(); iter != times.end(); ++iter) {
        delete iter->second;
    }
}

void TaskQueue::addTask(Task* newTask, unsigned int delay) {
    ++taskCount;
    ASSERT_NOT_EQUAL(0, delay);
    TaskTimesConstIter iter = times.find(currentTime + delay);
    TaskTime* taskTime = 0;
    if (iter == times.end()) {
        times[currentTime + delay] = taskTime = new TaskTime();
    } else {
        taskTime = iter->second;
    }
    taskTime->addTask(newTask);
}

void TaskQueue::runNextTime() {
    ++currentTime;
    TaskTimesIter iter = times.find(currentTime);
    if (iter != times.end()) {
        TaskTime* now = iter->second;
        runTasks(now);
        times.erase(iter);
        delete now;
    }    
}

void TaskQueue::runTasks(TaskTime* taskTime) {
    while (taskTime->first) {
        Task* current = taskTime->first;
        taskTime->first = current->next;
        unsigned int nextDelay = current->run();
        if (nextDelay != 0) {  //re-schedule
            addTask(current, nextDelay);
        } else {
            delete current;
        }
    }
}


void TaskQueue::printStatus(StringBuilder& buffer) const {
    char buf[8];
    snprintf(buf, sizeof(buf), "%.2f", taskCount / (currentTime / (float)seconds(1)));
    buffer << "  TaskQueue: " << taskCount << " tasks, " << buf << " tasks/sec\n";
}

